﻿using System.Text;

namespace XYAC.Framework.MonitorTireMaterialOnline.Extensions
{
    public class CrcCalculator
    {
        private static readonly uint[] Crc32Table;
        private static readonly ulong[] Crc64Table;
        private static readonly Crc128[] Crc128Table;

        static CrcCalculator()
        {
            Crc32Table = GenerateCrc32Table();
            Crc64Table = GenerateCrc64Table();
            Crc128Table = GenerateCrc128Table();
        }

        private static Crc128[] GenerateCrc128Table()
        {
            const ulong polynomialHigh = 0x42F0E1EBA9EA3693;
            const ulong polynomialLow = 0x42F0E1EBA9EA3693;
            var table = new Crc128[256];

            for (ulong i = 0; i < 256; i++)
            {
                ulong crcHigh = 0;
                ulong crcLow = i;

                for (ulong j = 8; j > 0; j--)
                {
                    if ((crcLow & 1) == 1)
                    {
                        crcLow = (crcLow >> 1) | (crcHigh << 63);
                        crcHigh = (crcHigh >> 1) ^ polynomialHigh;
                        crcLow ^= polynomialLow;
                    }
                    else
                    {
                        crcLow = (crcLow >> 1) | (crcHigh << 63);
                        crcHigh >>= 1;
                    }
                }
                table[i] = new Crc128 { High = crcHigh, Low = crcLow };
            }

            return table;
        }

        private static uint[] GenerateCrc32Table()
        {
            const uint polynomial = 0xEDB88320;
            var table = new uint[256];

            for (uint i = 0; i < 256; i++)
            {
                uint crc = i;
                for (uint j = 8; j > 0; j--)
                {
                    if ((crc & 1) == 1)
                    {
                        crc = (crc >> 1) ^ polynomial;
                    }
                    else
                    {
                        crc >>= 1;
                    }
                }
                table[i] = crc;
            }

            return table;
        }

        private static ulong[] GenerateCrc64Table()
        {
            const ulong polynomial = 0xC96C5795D7870F42;
            var table = new ulong[256];

            for (ulong i = 0; i < 256; i++)
            {
                ulong crc = i;
                for (ulong j = 8; j > 0; j--)
                {
                    if ((crc & 1) == 1)
                    {
                        crc = (crc >> 1) ^ polynomial;
                    }
                    else
                    {
                        crc >>= 1;
                    }
                }
                table[i] = crc;
            }

            return table;
        }

        public uint ComputeCrc32(byte[] bytes)
        {
            uint crc = 0xFFFFFFFF;

            foreach (var t in bytes)
            {
                var index = (byte)((crc & 0xFF) ^ t);
                crc = (crc >> 8) ^ Crc32Table[index];
            }

            return crc ^ 0xFFFFFFFF;
        }

        public ulong ComputeCrc64(byte[] bytes)
        {
            ulong crc = 0xFFFFFFFFFFFFFFFF;

            foreach (var t in bytes)
            {
                var index = (byte)((crc & 0xFF) ^ t);
                crc = (crc >> 8) ^ Crc64Table[index];
            }

            return crc ^ 0xFFFFFFFFFFFFFFFF;
        }
        public Crc128 ComputeCrc128(byte[] bytes)
        {
            ulong crcHigh = 0xFFFFFFFFFFFFFFFF;
            ulong crcLow = 0xFFFFFFFFFFFFFFFF;

            foreach (var t in bytes)
            {
                var index = (byte)((crcLow & 0xFF) ^ t);
                var tableValue = Crc128Table[index];
                crcLow = (crcLow >> 8) | (crcHigh << 56);
                crcHigh >>= 8;
                crcHigh ^= tableValue.High;
                crcLow ^= tableValue.Low;
            }

            return new Crc128
            {
                High = crcHigh ^ 0xFFFFFFFFFFFFFFFF,
                Low = crcLow ^ 0xFFFFFFFFFFFFFFFF
            };
        }


        public Crc128 ComputeCrc128(string strings)
        {
            var encoding = Encoding.UTF8.GetBytes(strings);
            return ComputeCrc128(encoding);
        }

        public ulong ComputeCrc64(string strings)
        {
            var encoding = Encoding.UTF8.GetBytes(strings);
            return ComputeCrc64(encoding);
        }

        public uint ComputeCrc32(string strings)
        {
            var encoding = Encoding.UTF8.GetBytes(strings);
            return ComputeCrc32(encoding);
        }
    }

}
